/* このプログラムはWeb ページの先頭部分に掲載するために作られている。 特にH1タグの位置からCANVASのheightを決めているところ。 */ //--Class 1/10 App class App { constructor( canvasId ) { this.timerMs = 100; this.cc = document.getElementById( canvasId ).getContext( "2d" ); this.cc.canvas.style.width = "100%"; this.cc.canvas.style.minHeight = "16px"; this.cc.canvas.width = 512; this.cc.canvas.app = this; this.scale = 2; this.horizon = 0.9; //---canvas臨時拡張メソッド this.cc.tmp = { circle : function( x, y, r, fillStyle, strokeStyle ) { this.cc.beginPath(); this.cc.arc( x, y, r, 0, 6.28, false ); this.cc.closePath(); if( fillStyle ) { this.cc.fillStyle = fillStyle; this.cc.fill(); } if( strokeStyle ) { this.cc.strokeStyle = strokeStyle; this.cc.stroke(); } }.bind( this ), line : function( sx, sy, ex, ey, color ) { this.cc.beginPath(); this.cc.moveTo( sx, sy ); this.cc.lineTo( ex, ey ); if( color ) this.cc.strokeStyle = color; this.cc.stroke(); }.bind( this ), clearAll : function() { this.cc.clearRect( - this.cc.canvas.width / this.scale / 2, this.cc.canvas.height * this.horizon / this.scale, this.cc.canvas.width / this.scale, - this.cc.canvas.height / this.scale ); //debug. 描画領域を確認したいとき if( 0 ) { this.cc.strokeStyle = "red"; this.cc.strokeRect( - this.cc.canvas.width / this.scale / 2, this.cc.canvas.height * this.horizon / this.scale, this.cc.canvas.width / this.scale, - this.cc.canvas.height / this.scale ); } }.bind( this ), }//this.cc.tmp //---各SVC_Part定義 let world = this.world = new SVC_Part( "world" ); world.addJoint( "世界>キャラ1", -100, 0 ); world.addJoint( "世界>kiseki1", 0, 100 ); world.imgd.visibility = true; world.imgd.type = "horizon"; let hara = new SVC_Top( "腹" ); hara.imgd.w = 18; hara.imgd.h = 15; hara.imgd.x = -12.5; hara.imgd.y = -15; hara.addJoint( "腹>胸", 0, 0 ); hara.addJoint( "腹>腰", 0, -12 ); let mune = new SVC_Part( "胸" ); mune.imgd.w = 20; mune.imgd.h = 20; mune.imgd.x = -12.5; mune.imgd.y = -5; mune.addJoint( "胸>首", 1, 14 ); mune.addJoint( "胸>左上腕", 1, 14 ); let jouwanL = new SVC_Part( "左上腕" ); jouwanL.imgd.w = 10; jouwanL.imgd.h = 23; jouwanL.imgd.x = -5; jouwanL.imgd.y = -23; jouwanL.addJoint( "左上腕>左前腕", 2, -20 ); let zenwanL = new SVC_Part( "左前腕" ); zenwanL.imgd.w = 10; zenwanL.imgd.h = 23; zenwanL.imgd.x = -7; zenwanL.imgd.y = -21; zenwanL.addJoint( "左前腕>左手", -2, -20 ); let teL = new SVC_Part( "左手" ); teL.imgd.w = 10; teL.imgd.h = 10; teL.imgd.x = -5; teL.imgd.y = -10; teL.addJoint( "つかみ", 0, -5 ); let kubi = new SVC_Part( "首" ); kubi.imgd.w = 10; kubi.imgd.h = 10; kubi.imgd.x = -6; kubi.imgd.y = -2; kubi.addJoint( "首>頭", 0, 6 ); let atama = new SVC_Part( "頭" ); atama.imgd.w = 20; atama.imgd.h = 20; atama.imgd.x = -13; atama.imgd.y = -1; let kosi = new SVC_Part( "腰" ); kosi.imgd.w = 20; kosi.imgd.h = 15; kosi.imgd.x = -12.5; kosi.imgd.y = -13; kosi.addJoint( "腰>左もも", -2.5, -10 ); let momoL = new SVC_Part( "左もも" ); momoL.imgd.w = 18; momoL.imgd.h = 30; momoL.imgd.x = -10; momoL.imgd.y = -28; momoL.addJoint( "左もも>左すね", -2.5, -27.5 ); let suneL = new SVC_Part( "左すね" ); suneL.imgd.w = 18; suneL.imgd.h = 30; suneL.imgd.x = -6.5; suneL.imgd.y = -28; suneL.addJoint( "左すね>左かかと", 2.5, -25 ); let kakatoL = new SVC_Part( "左かかと" ); kakatoL.imgd.w = 20; kakatoL.imgd.h = 10; kakatoL.imgd.x = -12; kakatoL.imgd.y = -6; kakatoL.addJoint( "左かかと>左つま先", -10, -2 ); kakatoL.addJoint( "接地", -10, -5 ); let tumasakiL = new SVC_Part( "左つま先" ); tumasakiL.imgd.w = 10; tumasakiL.imgd.h = 7; tumasakiL.imgd.x = -10; tumasakiL.imgd.y = -4; tumasakiL.addJoint( "接地", -10, 0 ); let stick = new SVC_Part( "棒" ); stick.imgd.w = 100; stick.imgd.h = 4; stick.imgd.x = 0; stick.imgd.y = 0; stick.addJoint( "先", 90, 2 ); stick.addJoint( "真ん中", 50, 2 ); this.char1 = hara; //---SVC_Part同士連結 world.connect( "世界>キャラ1", hara ); //キャラ1 kubi.connect( "首>頭", atama ); mune.connect( "胸>首", kubi ); // teL.connect( "つかみ", stick, "先" ); zenwanL.connect( "左前腕>左手", teL ); jouwanL.connect( "左上腕>左前腕", zenwanL ); mune.connect( "胸>左上腕", jouwanL ); hara.connect( "腹>胸", mune ); hara.connect( "腹>腰", kosi ); kosi.connect( "腰>左もも", momoL ); momoL.connect( "左もも>左すね", suneL ); suneL.connect( "左すね>左かかと", kakatoL ); kakatoL.connect( "左かかと>左つま先", tumasakiL ); //---ポーズ定義開始 let tick = 3.14 / 10; //---kiseki this.kiseki = new Kiseki( { f : function( x ) { return -x * x; }, sx : -10, ex : 0, step : 0.5, x : null, y : 100, zoomX : 1.6, zoomY : 0.1, zoom : 2, visibility : true, } ); this.kiseki2 = new Kiseki( { f : function( x ) { return -2 * x * x; }, sx : -10, ex : 14, step : 0.5, x : -50, y : 100, zoomX : 1.6, zoomY : 0.2, zoom : 2, visibility : true, } ); //---poses this.poses = { chokuritu : new Pose( 1000, this.timerMs, { list : [ //数値: 減らす時計回り 増やす反時計まわり [ hara, "rotation", 0 ], [ atama, "rotation", tick * -.5, 1000 ], [ kubi, "rotation", tick * 0, 1000 ], [ mune, "rotation", tick * .5, 1000 ], [ kosi, "rotation", tick * -1, 1000 ], [ momoL, "rotation", tick * -1.5, 1000 ], [ suneL, "rotation", tick * 2.5, 1000 ], [ kakatoL, "rotation", tick * -1.5, 1000 ], [ tumasakiL, "rotation", tick * 0, 1000 ], [ jouwanL, "rotation", tick * -4, 1000 ], [ zenwanL, "rotation", tick * -2, 1000 ], [ teL, "rotation", tick * -1, 1000 ], ], } ), kagami : new Pose( 500, this.timerMs, { list : [ //数値: 減らす時計回り 増やす反時計まわり [ hara, "rotation", 0 ], [ atama, "rotation", tick * -.5 ], [ kubi, "rotation", tick * 0 ], [ mune, "rotation", tick * .5 ], [ kosi, "rotation", tick * -1 ], [ momoL, "rotation", tick * -1.5 ], [ suneL, "rotation", tick * 2.5 ], [ kakatoL, "rotation", tick * -1.5 ], [ tumasakiL, "rotation", tick * 0 ], [ jouwanL, "rotation", tick * -4 ], [ zenwanL, "rotation", tick * -2 ], [ teL, "rotation", tick * -1 ], ], } ), senobi : new Pose( 300, this.timerMs, { list : [ //数値: 減らす時計回り 増やす反時計まわり [ atama, "rotation", tick * -1.5 ], [ kubi, "rotation", tick * -1 ], [ mune, "rotation", tick * -0.5 ], [ kosi, "rotation", tick * 0.5 ], [ momoL, "rotation", tick * 0.5 ], [ suneL, "rotation", tick * 0 ], [ kakatoL, "rotation", tick * 2.5 ], [ tumasakiL, "rotation", tick * -3 ], [ jouwanL, "rotation", tick * -12 ], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * -1 ], ], } ), bridge : new Pose( 1000, this.timerMs, { start : function() { if( ! this.kiseki.flg ) { this.kiseki.x = hara.abs.x; this.kiseki.y = hara.abs.y; this.kiseki.flg = true; } hara.adjust2( hara, "腹>腰", this.kiseki.cursor, -tick * 0.5 ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ this.kiseki, "count", this.kiseki.stepCountMax ], [ hara, "rotation", -3.14 / 2 ], [ atama, "rotation", tick * -1 ], [ kubi, "rotation", tick * -2 ], [ mune, "rotation", -3.14 /10 ], [ kosi, "rotation", 3.14 /10 ], [ momoL, "rotation", 3.14 /6 ], [ suneL, "rotation", 3.14 /4 ], [ kakatoL, "rotation", 3.14 /4 ], [ tumasakiL, "rotation", -3.14 /4 ], [ jouwanL, "rotation", tick * -10 ], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * 0 ], ], } ), sakadachi : new Pose( 1000, this.timerMs, { start : function() { this.kiseki.x = hara.jointHash[ "腹>腰" ].abs.x; this.kiseki.y = hara.jointHash[ "腹>腰" ].abs.y; this.kiseki.sx = 0; this.kiseki.ex = 15; this.kiseki.flg = true; this.kiseki.update(); // hara.adjust2( hara, "腹>腰", this.kiseki.cursor, -tick * 0.5 ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ this.kiseki, "count", 26 ], [ hara, "rotation", -2.9 ], [ atama, "rotation", tick * -1 ], [ kubi, "rotation", tick * -2 ], [ mune, "rotation", tick * -0.5 ], [ kosi, "rotation", tick * 0.5 ], [ momoL, "rotation", tick * 0 ], [ suneL, "rotation", tick * 0 ], [ kakatoL, "rotation", 3.14 /4 ], [ tumasakiL, "rotation", tick * 1 ], [ jouwanL, "rotation", tick * -10], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * -0.5 ], ], }, ), sakadachi2 : new Pose( 200, this.timerMs, { start : function() { let point = new Point( teL.abs.x, teL.abs.y - 5 ); hara.adjust2( teL, "つかみ", point, -tick * 0.5 ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ hara, "rotation", -3.14 ], [ atama, "rotation", tick * -1 ], [ kubi, "rotation", tick * -2 ], [ mune, "rotation", tick * -0.5 ], [ kosi, "rotation", tick * 0 ], [ momoL, "rotation", tick * 0 ], [ suneL, "rotation", tick * 0 ], [ kakatoL, "rotation", 3.14 /4 ], [ tumasakiL, "rotation", tick * 1 ], [ jouwanL, "rotation", tick * -10], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * -0.5 ], ], }, ), a_10en_ochiteru : new Pose( 1000, this.timerMs, { start : function() { hara.releaseAdjust(); hara.moveTo( hara.abs.x - 3, hara.abs.y + 10 ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ hara, "rotation", -5.25 ], [ atama, "rotation", tick * .5 ], [ kubi, "rotation", tick * 0 ], [ mune, "rotation", tick * 1 ], [ kosi, "rotation", tick * -1.5 ], [ momoL, "rotation", tick * -2.5 ], [ suneL, "rotation", tick * 0 ], [ kakatoL, "rotation", tick * 0 ], [ tumasakiL, "rotation", tick * 0 ], [ jouwanL, "rotation", tick * -5 ], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * -1 ], ], }, ), ojigi : new Pose( 250, this.timerMs, { start : function() { let point = new Point( tumasakiL.abs.x - 9, tumasakiL.abs.y + 3 ); hara.adjust2( tumasakiL, "接地", point, tick * 0 ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ hara, "rotation", -5.25 ], [ atama, "rotation", tick * -.5 ], [ kubi, "rotation", tick * 0 ], [ mune, "rotation", tick * 1 ], [ kosi, "rotation", tick * -1.5 ], [ momoL, "rotation", tick * -3 ], // [ suneL, "rotation", tick * 0.5 ], // [ kakatoL, "rotation", tick * -0.5 ], // [ tumasakiL, "rotation", tick * 0 ], [ jouwanL, "rotation", tick * -5 ], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * -1 ], ], }, ), waai : new Pose( 1000, this.timerMs, { start : function() { if( ! this.kiseki2.flg ) { hara.releaseAdjust(); this.kiseki2.x = hara.abs.x + 5; this.kiseki2.y = hara.abs.y - 5; this.kiseki2.flg = true; } hara.adjust2( hara, "腹>腰", this.kiseki2.cursor, 0 ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ this.kiseki2, "count", 5 ], [ atama, "rotation", 0], [ kubi, "rotation", 0 ], [ mune, "rotation", -0.12 ], [ hara, "rotation", -6.45 ], [ kosi, "rotation", 0.12 ], [ momoL, "rotation", 0 ], [ suneL, "rotation", 0 ], [ kakatoL, "rotation", tick * 4 ], [ tumasakiL, "rotation", tick * 1 ], [ jouwanL, "rotation", tick * -10 ], [ zenwanL, "rotation", tick * -.5 ], [ teL, "rotation", tick * -1 ], ], }, ), sinsin : new Pose( 1000, this.timerMs, { start : function() { console.log( "hara.abs.rotation", hara.abs.rotation ); }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ this.kiseki2, "count", 15 ], [ hara, "rotation", -10 ], [ atama, "rotation", tick * -.5 ], [ kubi, "rotation", tick * -.5 ], [ mune, "rotation", tick * -1 ], [ kosi, "rotation", tick * .8 ], [ momoL, "rotation", tick * 1.5 ], [ suneL, "rotation", tick * 1 ], [ kakatoL, "rotation", tick * 4 ], [ tumasakiL, "rotation", tick * 1 ], [ jouwanL, "rotation", tick * 1.5 ], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * 0 ], ], } ), kagami3 : new Pose( 1000, this.timerMs, { start : function() { }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ this.kiseki2, "count", 39.5 ], [ hara, "rotation", -11.45 ], [ atama, "rotation", tick * 0 ], [ kubi, "rotation", tick * 0 ], [ mune, "rotation", tick * 1 ], [ kosi, "rotation", tick * -1.5 ], [ momoL, "rotation", tick * -2.5 ], [ suneL, "rotation", tick * 0 ], [ kakatoL, "rotation", tick * 0 ], [ tumasakiL, "rotation", tick * 0 ], [ jouwanL, "rotation", tick * -2 ], [ zenwanL, "rotation", tick * 0 ], [ teL, "rotation", tick * -1 ], ], }, ), chokuritu2 : new Pose( 1000, this.timerMs, { start : function() { let point = new Point( tumasakiL.abs.x - 10, tumasakiL.abs.y + 1.5 ); hara.adjust2( tumasakiL, "接地", point, 0 ); tumasakiL.rotation = 0; this.testflg = true; }, list : [ //数値: 減らす時計回り 増やす反時計まわり [ atama, "rotation", tick * -.25 ], [ kubi, "rotation", tick * -.25 ], [ hara, "rotation", -3.15 * 4 ], [ mune, "rotation", tick * -.25 ], [ kosi, "rotation", tick * .25 ], [ momoL, "rotation", tick * .25 ], [ suneL, "rotation", 0 ], [ kakatoL, "rotation", -.12 ], [ tumasakiL, "rotation", 0 ], [ jouwanL, "rotation", tick * -10.5 ], [ zenwanL, "rotation", tick * -1 ], ], }, ), }; //---ポーズ定義終了 //描画登録 this.drawings = new Array(); this.drawings.push( world ); this.drawings.push( this.kiseki ); this.drawings.push( this.kiseki2 ); //---リサイズ対応 let resizex = function( e ) { console.log( "resizex()" ); let canvas = this.cc.canvas; //(canvasの物理領域について) //ウィンドウリサイズに伴うcanvas高さ変更 let H = document.getElementsByTagName( "H1" )[ 0 ]; if( H ) { canvas.style.height = H.offsetTop + "px"; } else { canvas.style.height = window.innerHeight - 48 + "px"; } //※なお、canvas.style.width は100% //(canvasの論理領域について) //canvas解像度変更 let cr = canvas.getBoundingClientRect(); //ウィンドウが縦長にしろ横長にしろ少なくともこの解像度を持たせる let minW = 512; let minH = 448; //minW, minH の矩形は、現在のcanvas物理領域に、 if( cr.width / minW > cr.height / minH ) { console.log( "縦フィットする" ); let pixels = cr.height / minH; //縦の1論理ピクセル当たりの物理ピクセル数 canvas.width = cr.width / pixels; //同じ物理ピクセルで割って解像度を得る。 canvas.height = minH; } else { console.log( "横フィットする" ); let pixels = cr.width / minW; canvas.width = minW; canvas.height = cr.height / pixels; } //中心位置 let transX = canvas.width / this.scale / 2; let transY = -canvas.height / this.scale * this.horizon; this.cc.scale( this.scale, -this.scale ); this.cc.translate( transX, transY ); this.draw( this.cc ); }.bind( this ); //resizex() // addEventListener( "resize", resizex ); //---実行 this.anms = new Array(); let to = world.jointHash[ "世界>キャラ1" ]; hara.adjust( tumasakiL, "接地", to.x, to.y, 0 ); world.calc(); resizex(); //posesからanmを作りそれぞれをつなげる let firstAnm; let beforeAnm; for( let name in this.poses ) { let anm = this.poses[ name ].getAnmGroup( name ); anm.next = null; //check. if( ! beforeAnm ) { firstAnm = anm; beforeAnm = anm; continue; } beforeAnm.next = anm; beforeAnm = anm; } this.anms.push( firstAnm ); }//constructor //全体を最初からやり直す reset( e ) { this.stop(); if( window.apps ) { let idx = window.apps.indexOf( this ); window.apps.splice( idx, 1 ); window.app = new App( this.cc.canvas.id ); window.app.start(); window.apps.push( window.app ); } else { window.app = new App( this.cc.canvas.id ); window.app.start(); } } //-- Method getArray (App 1/5) getArray( svc_part, array ) { array.push( svc_part ); for( let i = 0; i < svc_part.children.length; i++ ) { let child = svc_part.children[ i ]; this.getArray( child, array ); } } //-- Method frame (App 2/5) async frame() { for( let i = 0; i < this.anms.length; i++ ) { let anm = this.anms[ i ]; //check. アニメの事前関数実行 if( ! anm.startFlg ) { anm.startFlg = true; anm.start.call( this ); anm.useNow(); console.log( "A start1", anm.name ); } anm.frame.call( anm ); //check. アニメの終了 if( anm.endFlg ) { this.anms.splice( i, 1 ); i--; anm.end(); //アニメの事後関数実行 console.log( "A end", anm.name ); //次に続くアニメについて if( anm.next ) { anm.next.start.call( this ); if( this.testflg ) { await delay( 500 ); this.testflg = false; } anm.next.startFlg = true; this.anms.push( anm.getNext() ); console.log( "A start2", anm.next.name ); } } }//for anms this.world.calc(); this.draw( this.cc ); }//frame() //-- Method start (App 3/5) start() { switch( "" ) { case "key": onkeydown = this.frame.bind( this ); break; case "one": this.draw( this.cc ); break; case "slow": this.timerId = setInterval( this.frame.bind( this ), 500 ); break; case "normal": default: this.timerId = setInterval( this.frame.bind( this ), this.timerMs ); break; } } //-- Method stop (App 4/5) stop() { clearInterval( this.timerId ); } //-- Method draw (App 5/5) draw( cc ) { this.cc.tmp.clearAll(); for( let i = 0; i < this.drawings.length; i++ ) { let drawing = this.drawings[ i ]; //check. if( ! drawing.visibility ) continue; drawing.draw( cc ); } if( 0 ) { //画面の中心 cc.tmp.circle( 0, 0, 2, "red", "red" ); } } }//App //--Class 2/10 SVC_Part class SVC_Part { constructor( name ) { this.name = name; this.NORMAL_MODE = 0; this.ADJUST_MODE = 1; this.mode = this.NORMAL_MODE; this.visibility = true; this.rotation = 0; //as A this.abs = new Object(); this.imgd = new TestRect( "rect" ); this.jointHash = new Object(); this.jointHash[ "原点" ] = new Joint( "原点", 0, 0 ); this.currentJoint = this.jointHash[ "原点" ]; this.parentJoint = null; this.parent = null; this.children = new Array(); this.debug1 = false; } //-- Method addJoint (SVC_Part 1/8) addJoint( name, x, y ) { this.jointHash[ name ] = new Joint( name, x, y ); } //-- Method connect (SVC_Part 2/8) connect( jointName, child, childJointName ) { //check. 自分自身を子にしようとした if( this == child ) { alert( this.name + "は、connect()において、自分自身を子にしようとした." ); return; } //check. 省略時は原点 if( childJointName == null ) { childJointName = "原点"; } //check. そのジョイント名は存在しない。 if( ! this.jointHash[ jointName ] ) { let mes = "The joint name does not exist."; mes += "joint name: " + this.name + "." + jointName; mes += "at connect()"; alert( mes ); } //check. そのジョイント名は存在しない。 if( ! child.jointHash[ childJointName ] ) { let mes = "The joint name does not exist."; mes += "joint name: " + child.name + "." + childJointName; mes += "at connect()"; alert( mes ); } this.children.push( child ); child.parent = this; child.parentJoint = this.jointHash[ jointName ]; child.currentJoint = child.jointHash[ childJointName ]; } //-- Method resetAllRotation (SVC_Part 3/8) resetAllRotation() { this.rotation = 0; //子 this.children.map( child => child.resetAllRotation() ); } //-- Method calc (SVC_Part 4/8) calc() { //自身の絶対座標を求める。(仮) if( ! this.parent ) { //親がない場合は this.abs.x = 0; this.abs.y = 0; this.abs.rotation = this.rotation; } else { //親がある場合は //check. if( ! this.parentJoint ) { alert( "undefined parentJoint at " + this.name ); } if( this instanceof SVC_Top && this.mode == this.NORMAL_MODE ) { this.abs.x = this.x; this.abs.y = this.y; } else { this.abs.x = this.parentJoint.abs.x; this.abs.y = this.parentJoint.abs.y; } this.abs.rotation = this.rotation + this.parent.abs.rotation; //as C = A + B } //各jointの絶対座標を求める。 for( let name in this.jointHash ) { let joint = this.jointHash[ name ]; let x = joint.x - this.currentJoint.x; let y = joint.y - this.currentJoint.y; let res = this.mathRotate( x, y, this.abs.rotation ); joint.abs.x = this.abs.x + res.X; joint.abs.y = this.abs.y + res.Y; } //子 this.children.map( child => child.calc() ); }//calc() //-- Method calc2 (SVC_Part 5/8) calc2( adjustX, adjustY, cx, cy, adjustR ) { //「仮絶対座標」にadjustを加えて「絶対座標」にする this.abs.x += adjustX; this.abs.y += adjustY; //「仮絶対回転」にadjustを加えて「絶対回転」にする if( adjustR != null ) { this.abs.rotation += adjustR; //as E = C + D } //座標をadjust回転 let res = this.mathRotateC( cx, cy, this.abs.x, this.abs.y, adjustR ); this.abs.x = res.X; this.abs.y = res.Y; //各jointも座標をadjust回転 for( let name in this.jointHash ) { let joint = this.jointHash[ name ]; //「仮絶対座標」にadjustを加えて「絶対座標」にする joint.abs.x += adjustX; joint.abs.y += adjustY; //(※jointにrotaion、abs.rotaionはない) //座標をadjust回転 let res = this.mathRotateC( cx, cy, joint.abs.x, joint.abs.y, adjustR ); joint.abs.x = res.X; joint.abs.y = res.Y; } //子 this.children.map( child => child.calc2( adjustX, adjustY, cx, cy, adjustR ) ); }//calc2() //-- Method draw (SVC_Part 6/8) draw( cc ) { //自身を描画 cc.save(); cc.translate( this.abs.x, this.abs.y ); cc.rotate( this.abs.rotation ); let x = this.imgd.x - this.jointHash[ this.currentJoint.name ].x; let y = this.imgd.y - this.jointHash[ this.currentJoint.name ].y; cc.translate( x, y ); this.imgd.draw( cc ); cc.restore(); //子 this.children.map( child => child.draw( cc ) ); //debug. if( this.debug1 ) { //各jointを○で示す for( let name in this.jointHash ) { let joint = this.jointHash[ name ]; cc.save(); cc.translate( joint.abs.x, joint.abs.y ); cc.scale( 1, -1 ); // cc.globalAlpha = .3; cc.tmp.circle( 0, 0, 2, null, "red" ); cc.font = "6px''"; cc.fillStyle = "blue"; cc.fillText( name, 8, 0 ); cc.restore(); } } }//draw() //-- Method mathRotate (SVC_Part 7/8) mathRotate( x, y, theta2 ) { let theta1 = Math.atan2( y, x ); let hankei = Math.sqrt( x * x + y * y ); return { X : Math.cos( theta1 + theta2 ) * hankei, Y : Math.sin( theta1 + theta2 ) * hankei, } } //-- Method mathRotateC (SVC_Part 8/8) mathRotateC( cx, cy, x, y, theta2, flg ) { x -= cx; y -= cy; let theta1 = Math.atan2( y, x ); let hankei = Math.sqrt( x * x + y * y ); return { X : Math.cos( theta1 + theta2 ) * hankei + cx, Y : Math.sin( theta1 + theta2 ) * hankei + cy, } } }//SVC_Part //--Class 3/10 SVC_Top class SVC_Top extends SVC_Part { constructor( name ) { super( name ); this.x = 0; this.y = 0; this.adjustFrom = null; this.adjustFromJointName = null; this.adjust_toX = null; this.adjust_toY = null; this.adjust_toR = null; } //-- Method adjust (SVC_Top 1/5) adjust( adjustFrom, adjustFromJointName, toX, toY, r ) { //たとえば、足を地面に接地する等 //check. adjustFromJointName の存在チェック if( typeof adjustFrom.jointHash[ adjustFromJointName ] === "undefined" ) { console.log( adjustFrom.name + " does'nt have the name: " + adjustFromJointName + " at adjust()" ); } this.adjustFrom = adjustFrom; this.adjustFromJointName = adjustFromJointName; this.adjust_toX = toX; this.adjust_toY = toY; this.adjust_toR = r; this.mode = this.ADJUST_MODE; } //-- Method adjust2 (SVC_Top 2/5) adjust2( adjustFrom, adjustFromJointName, toPoint, r ) { //たとえば、足を地面に接地する等 //check. adjustFromJointName の存在チェック if( typeof adjustFrom.jointHash[ adjustFromJointName ] === "undefined" ) { console.log( adjustFrom.name + " does'nt have the name: " + adjustFromJointName + " at adjust()" ); } this.adjustFrom = adjustFrom; this.adjustFromJointName = adjustFromJointName; this.adjustToPoint = toPoint; this.adjust_toR = r; this.mode = this.ADJUST_MODE; } //-- Method releaseAdjust (SVC_Top 3/5) releaseAdjust() { this.mode = this.NORMAL_MODE; } //-- Method moveTo (SVC_Top 4/5) moveTo( x, y ) { this.x = x; this.y = y; this.mode = this.NORMAL_MODE; } //-- Method calc (SVC_Top 5/5) calc() { super.calc(); //この行で子孫はすべてcalc()される。 //たとえば、足を地面に接地する等 if( this.mode == this.ADJUST_MODE ) { if( this.adjustToPoint ) { //adjust2()版 let fromJoint = this.adjustFrom.jointHash[ this.adjustFromJointName ]; let adjustX = this.adjustToPoint.x - fromJoint.abs.x; let adjustY = this.adjustToPoint.y - fromJoint.abs.y; let cx = this.adjustToPoint.x; let cy = this.adjustToPoint.y; if( this.adjust_toR == null ) { this.calc2( adjustX, adjustY, cx, cy, null ); //この行で子孫はすべてcalc2()される。 } else { let adjustR = this.adjust_toR; //as D this.calc2( adjustX, adjustY, cx, cy, adjustR ); //この行で子孫はすべてcalc2()される。 } } else { //adjust()版 let fromJoint = this.adjustFrom.jointHash[ this.adjustFromJointName ]; let adjustX = this.adjust_toX - fromJoint.abs.x; let adjustY = this.adjust_toY - fromJoint.abs.y; let cx = this.adjust_toX; let cy = this.adjust_toY; let adjustR = this.adjust_toR - this.adjustFrom.abs.rotation; //as D this.calc2( adjustX, adjustY, cx, cy, adjustR ); //この行で子孫はすべてcalc2()される。 } }//if }//calc() }//SVC_Top //--Class 4/10 TestRect class TestRect { constructor( type ) { this.x = 0; this.y = 0; this.w = 10; this.h = 10; this.visibility = true; this.type = type; } //-- Method draw (TestRect 1/1) draw( cc ) { //check. if( ! this.visibility ) return; switch( this.type ) { case "rect": cc.strokeStyle = "black"; cc.strokeRect( 0, 0, this.w, this.h ); break; case "horizon": cc.fillStyle = "brown"; let w = 130; let h = 23; cc.fillRect( -w, -h, w * 2, h ); break; default: alert( "undefined type: '" + this.type + "'" ); } } }//class TestRect //--Class 5/10 Joint class Joint { constructor( name, x, y ) { this.name = name; this.x = x; this.y = y; this.abs = { x : null, y : null, } } } //--Class 6/10 Point class Point { constructor( x, y ) { this.x = x; this.y = y; } } //--Class 7/10 Pose class Pose { constructor( ms, timerMs, anmdata ) { this.ms = ms; this.timerMs = timerMs; this.anmdata = anmdata; this.next = null; } //-- Method getAnmGroup (Pose 1/1) getAnmGroup( name ) { let anmGroup = new AnmGroup(); anmGroup.name = name; for( let i = 0; i < this.anmdata.list.length; i++ ) { let data = this.anmdata.list[ i ]; let object = data[ 0 ]; let member = data[ 1 ]; let endValue = data[ 2 ]; let anm = new Anm( object, member, endValue, this.ms, this.timerMs ); anmGroup.children.push( anm ); //check. if( this.anmdata.start ) anmGroup.start = this.anmdata.start; if( this.anmdata.end ) anmGroup.end = this.anmdata.end; }//for return anmGroup; }//getAnmGroup() }//class Pose //--Class 8/10 Anm class Anm { constructor( object, member, endValue, ms, timerMs ) { this.name = "**"; this.object = object; this.member = member; this.endValue = endValue; this.ms = ms; this.timerMs = timerMs; this.useNow(); } //-- Method useNow (Anm 1/2) useNow() { this.startValue = this.object[ this.member ]; this.dir = this.endValue > this.startValue ? 1 : -1; let count = this.ms / this.timerMs; this.step = Math.abs( this.endValue - this.startValue ) / count; this.endFlg = this.endValue == this.startValue; } //-- Method frame (Anm 2/2) frame() { this.object[ this.member ] += this.step * this.dir; //check. if( ( this.dir > 0 && this.object[ this.member ] >= this.endValue ) || ( this.dir < 0 && this.object[ this.member ] <= this.endValue ) ) { this.object[ this.member ] = this.endValue; this.endFlg = true; } } start() {} end() {} }//class Anm //--Class 9/10 AnmGroup class AnmGroup { constructor( name ) { this.name = name; this.children = new Array(); } //-- Method frame (AnmGroup 1/4) frame() { //子のframe()を実行する。 this.children.map( child => child.frame() ); //子がすべて終了なら、自身は終了。 this.endFlg = true; for( let i = 0; i < this.children.length; i++ ) { if( this.children[ i ].endFlg == false ) { this.endFlg = false; break; } } } //-- Method useNow (AnmGroup 2/4) useNow() { //子のuseNow()を実行する。 this.children.map( child => child.useNow() ); } //-- Method getNext (AnmGroup 3/4) getNext() { //check. if( ! this.next ) { return null; } this.next.useNow(); return( this.next ); } //-- Method start (AnmGroup 4/4) start() {} end() {} }//class AnmGroup //--Class 10/10 Kiseki class Kiseki { constructor( args ) { this.f = function( x ) { return x; }; this.sx = -10; //グラフ上の始点sx this.ex = 10; //グラフ上の終点sx this.step = 1; //グラフ上のxのステップ this.x = null; //画面上のx。ここにグラフ上の始点sxが来る。 this.y = null; //画面上のy。ここにグラフ上の始点syが来る。 this.zoomX = 1; //x方向だけ引き伸ばしたいときに利用 this.zoomY = 1; //y方向だけ引き伸ばしたいときに利用 this.zoom = 1; //全体的に拡大したいときに利用 this.visibility = false; Object.assign( this, args ); this.cursor = new Point( 0, 0 ); Object.seal( this.cursor ); //this.cursorは別のインスタンスにならない this.update(); } //-- Method update (Kiseki 1/3) update() { //画面上の座標 this.x, this.y と、グラフの始点を合わせるための補正値 this.adjustX = this.sx * this.zoomX * this.zoom; this.adjustY = this.f( this.sx ) * this.zoomY * this.zoom; this.stepCountMax = Math.abs( this.ex - this.sx ) / this.step; this.dir = this.ex >= this.sx ? 1 : -1; this.count = 0; } //this.countについて set count( index ) { this._count = index; let graphX = this.sx + index * this.step * this.dir; //グラフ上のx this.cursor.x = this.x + graphX * this.zoomX * this.zoom - this.adjustX; this.cursor.y = this.y + this.f( graphX ) * this.zoomY * this.zoom - this.adjustY; } get count() { return this._count; } //-- Method getPointByStep (Kiseki 2/3) getPointByStep( stepCount ) { let graphX = this.sx + stepCount * this.step * this.dir; //グラフ上のx return { //画面上のx,y graphX : graphX, x : this.x + graphX * this.zoomX * this.zoom - this.adjustX, y : this.y + this.f( graphX ) * this.zoomY * this.zoom - this.adjustY, }; } //-- Method draw (Kiseki 3/3) draw( cc ) { //始点に赤い○を描く cc.tmp.circle( this.x, this.y, 4, null, "red" ); let g = this.getPointByStep( this.count ); cc.tmp.circle( g.x, g.y, 4, null, "blue" ); for( let i = 0; i <= this.stepCountMax; i++ ) { let g = this.getPointByStep( i ); //check. 原点のとき、座標軸を描く if( g.graphX == 0 ) { cc.tmp.line( g.x - 50, g.y, g.x + 50, g.y, "lightgray" ); cc.tmp.line( g.x, g.y - 50, g.x, g.y + 50, "lightgray" ); } //青い薄い点 cc.tmp.circle( g.x, g.y, 1, "lightblue" ); } }//draw() }//class Kiseki function delay( ms ) { return new Promise( function( tellOk ) { setTimeout( tellOk, ms ); } ); }